package com.ae.shield.common.recipe.assembly;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.ae.shield.ModLib;
import com.ae.shield.common.fluid.FluidHelper;
import com.ae.shield.common.items.shieldFittings.makers.ShieldMakerBase;
import com.ae.shield.common.recipe.ModRecipesType;
import com.ae.shield.common.enchantment.shieldSuffix.SuffixList;
import com.ae.shield.common.tag.TagHelper;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ITag;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.registries.ForgeRegistryEntry;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;

public class ShieldUpgradeAssemblyRecipe extends AssemblyRecipe {

    private final int newLevel;
    private final int oldLevel;

    public ShieldUpgradeAssemblyRecipe(ResourceLocation id, int newLevel, int oldLevel, Fluid fluid, ITag<Fluid> fluidTag, int fluidCount,  int energy, int tick, Ingredient... itemInputs) {
        super(id, getResult(itemInputs[0].getMatchingStacks()[0].copy(), newLevel), fluid, fluidTag, fluidCount, energy, tick, itemInputs);
        this.newLevel = newLevel;
        this.oldLevel = oldLevel;
    }

    private static ItemStack getResult(ItemStack stack, int lvl) {
        ItemStack s = stack.copy();
        s.getOrCreateTag().remove("Enchantments");
        s.addEnchantment(SuffixList.LEVEL_UP.get(), lvl);
        ((ShieldMakerBase)s.getItem()).reloadProperty(s);
        return s;
    }

    public boolean matches(FluidStack fluid, NonNullList<ItemStack> stacks) {
        ItemStack stack0 = stacks.get(0);

        if (itemInputs.get(0).test(stack0)) {
            int level = EnchantmentHelper.getEnchantmentLevel(SuffixList.LEVEL_UP.get(), stack0);

            if (level == oldLevel) {
                return super.matches(fluid, stacks);
            }
        }
        return false;
    }

    public int getNewLevel() {
        return newLevel;
    }

    public int getOldLevel() {
        return oldLevel;
    }

    public static ResourceLocation getTypeId() {
        return new ResourceLocation(ModLib.MOD_ID, "shield_upgrade_assembly");
    }

    @Nonnull
    @Override
    public IRecipeSerializer<?> getSerializer() {
        return ModRecipesType.SHIELD_UPGRADE_ASSEMBLY_SERIALIZER.get();
    }

    public static class ShieldUpgradeAssemblyRecipeType implements IRecipeType<ShieldUpgradeAssemblyRecipe> {
        @Override
        public String toString() {
            return "shield_upgrade_assembly";
        }
    }

    public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<ShieldUpgradeAssemblyRecipe> {

        @Nonnull
        @Override
        public ShieldUpgradeAssemblyRecipe read(@Nonnull ResourceLocation id, @Nonnull JsonObject json) {
            int oldLvl = JSONUtils.getInt(json, "old_lvl");
            int newLvl = JSONUtils.getInt(json, "new_lvl");

            Fluid fluid = null;
            ITag<Fluid> fluidTag = null;
            int fluidCount = 0;

            if (JSONUtils.hasField(json, "fluid_ingredient")) {
                JsonObject fluidJson = JSONUtils.getJsonObject(json, "fluid_ingredient");
                fluid = FluidHelper.deserializeFluid(fluidJson);

                if (fluid == null) {
                    fluidTag = TagHelper.deserializeFluidTag(fluidJson);
                }

                fluidCount = JSONUtils.getInt(fluidJson, "count");
            }

            int energy = JSONUtils.getInt(json, "energy_per_tick");
            int tick = JSONUtils.getInt(json, "processing_time");

            JsonArray ingredients = JSONUtils.getJsonArray(json, "ingredients");
            Ingredient backing = Ingredient.deserialize(JSONUtils.getJsonArray(json, "backing"));
            ItemStack shield = backing.getMatchingStacks()[0];

            if (oldLvl != 0) {
                shield = getResult(shield, oldLvl);
            }

            Ingredient shieldIng = Ingredient.fromStacks(shield);
            List<Ingredient> inputs = new ArrayList<>();
            inputs.add(shieldIng);
            for (JsonElement e : ingredients) {
                inputs.add(Ingredient.deserialize(e));
            }

            return new ShieldUpgradeAssemblyRecipe(id, newLvl, oldLvl, fluid, fluidTag, fluidCount, energy, tick, inputs.toArray(new Ingredient[0]));
        }

        @Nullable
        @Override
        public ShieldUpgradeAssemblyRecipe read(@Nonnull ResourceLocation id, @Nonnull PacketBuffer buffer) {
            int fluidFlag = buffer.readInt();
            Fluid fluid = null;
            ITag<Fluid> fluidTag = null;

            switch (fluidFlag) {
                case 1:
                    FluidStack fluidStack = buffer.readFluidStack();
                    fluid = fluidStack.getFluid();
                    break;
                case 2:
                    fluidTag = FluidTags.createOptional(buffer.readResourceLocation());
                    break;
            }

            int[] ints = buffer.readVarIntArray(5);
            Ingredient[] inputs = new Ingredient[buffer.readVarInt()];
            for (int i = 0; i < inputs.length; i++) {
                inputs[i] = Ingredient.read(buffer);
            }
            return new ShieldUpgradeAssemblyRecipe(id,ints[3], ints[4], fluid, fluidTag, ints[0], ints[1], ints[2], inputs);
        }

        @Override
        public void write(@Nonnull PacketBuffer buffer, ShieldUpgradeAssemblyRecipe recipe) {
            if (recipe.fluid != null) {
                buffer.writeInt(1);
                buffer.writeFluidStack(new FluidStack(recipe.fluid, 1));
            } else if (recipe.fluidTag != null) {
                buffer.writeInt(2);
                buffer.writeResourceLocation(((ITag.INamedTag<Fluid>) recipe.fluidTag).getName());
            } else {
                buffer.writeInt(0);
            }

            buffer.writeVarIntArray(new int[]{recipe.fluidCount, recipe.energy, recipe.tick, recipe.newLevel, recipe.oldLevel});
            buffer.writeVarInt(recipe.itemInputs.size());
            for (Ingredient input : recipe.getIngredients()) {
                input.write(buffer);
            }
        }
    }
}
